package com.rongji.cms.tools.file.utils;

import cn.hutool.core.io.FileUtil;
import com.rongji.cms.tools.file.config.LibreofficeProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.jodconverter.LocalConverter;
import org.jodconverter.office.LocalOfficeManager;
import org.jodconverter.office.OfficeException;
import org.jodconverter.office.OfficeManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author yang
 * @date 2022/11/21 14:09
 * @description 利用libreoffice来进行转pdf
 */

@Slf4j
@Component
public class PdfUtils {

    @Resource
    private LibreofficeProperties libreofficeProperties;


    /**
     * 通过jodconverter调用
     * 同步方式，特点是屏蔽了具体的命令细节，直接调用convert方法即可，并且是同步返回结果或者异常；
     *
     * @param srcPath
     * @param pdfFilePath
     */
    public void toPdfSync(String srcPath, String pdfFilePath) {
        log.info("开始进行pdf转换---------------------------------------");
        long start = System.currentTimeMillis();
        try {
            LocalOfficeManager.Builder builder = LocalOfficeManager.builder();
            builder.officeHome(libreofficeProperties.getHome());
            builder.portNumbers(libreofficeProperties.getPort());
            builder.taskExecutionTimeout(libreofficeProperties.getTaskExecutionTimeout()); // minute
            builder.taskQueueTimeout(libreofficeProperties.getTaskExecutionTimeout()); // hour
            OfficeManager build = builder.build();
            build.start();
            LocalConverter make = LocalConverter.make(build);
            make.convert(new File(srcPath)).to(new File(pdfFilePath)).execute();
            build.stop();
        } catch (OfficeException e) {
            log.error(e.getMessage(), e);
        }
        long end = System.currentTimeMillis();
        log.info("转换pdf文件耗时：" + (end - start) + "毫秒");
    }


    /**
     * PDF文件转PNG图片，全部页数
     *
     * @param pdfFilePath  pdf文件路径 物理路径
     * @param dstImgFolder 图片存放的文件夹
     * @param imagePDFName 图片名称,用转成的pdf的名称
     * @param dpi          dpi越大转换后越清晰，相对转换速度越慢 dpi为96,100,105,120,150,200中,105显示效果较为清晰,体积稳定,dpi越高图片体积越大,一般电脑显示分辨率为96
     * @return
     */
    public int pdf2Image(String pdfFilePath, String dstImgFolder, String imagePDFName, int dpi) {
        log.info("开始进行pdf转图片---------------------------------------");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        int pages = 1;
        PDDocument pdDocument = new PDDocument();
        try {
            File file = new File(pdfFilePath);
            while (!file.exists()) {
                log.info("文件未生成，等待1秒");
                Thread.sleep(1000);
            }
            pdDocument = PDDocument.load(file);

            PDFRenderer renderer = new PDFRenderer(pdDocument);
            /* dpi越大转换后越清晰，相对转换速度越慢 */
            pages = pdDocument.getNumberOfPages();
//            每个pdf文件单独建个文件夹,文件夹名称就是文件名,然后是按日期建上级目录
            StringBuffer imgFilePath = null;
            for (int i = 0; i < pages; i++) {
                String imgFilePathPrefix = dstImgFolder + imagePDFName;
                imgFilePath = new StringBuffer();
                imgFilePath.append(imgFilePathPrefix);
                imgFilePath.append("_");
                imgFilePath.append(String.valueOf(i + 1));
                imgFilePath.append(".png");
//                File dstFile = new File(imgFilePath.toString());
                File dstFile = FileUtil.touch(imgFilePath.toString());
                BufferedImage image = renderer.renderImageWithDPI(i, dpi);
                ImageIO.write(image, "png", dstFile);
            }
            stopWatch.stop();

            log.info("PDF文档转PNG图片成功！耗时:" + stopWatch.getTotalTimeSeconds() + "毫秒");
        } catch (IOException | InterruptedException e) {
            log.info("-------------pdf转换出错");
            log.error(e.getMessage(), e);
        } finally {
            try {
                pdDocument.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return pages;
    }

    /**
     * 命令行方式
     * 是异步调用的，回时，转换可能还在进行中，转换是否有异常也未可知
     * fixme 不方便排查问题，暂时不用这种方式
     *
     * @param filePath
     * @param targetFolder
     */
    @Async
    public void toPdf(String filePath, String targetFolder) {
        long start = System.currentTimeMillis();
        String srcPath = filePath;
        String desPath = targetFolder;
        log.info("源文件：" + filePath);
        log.info("目标文件夹：" + targetFolder);
        String command = "";
        String osName = System.getProperty("os.name");
        log.info("系统名称：" + osName);
        if (osName.contains("Windows")) {
//            command = "cmd /c start soffice --headless --invisible --convert-to pdf:writer_pdf_Export " + inputFile + " --outdir " + pdfFile;
//            command = "libreoffice --headless --invisible --convert-to pdf:writer_pdf_Export " + inputFile + " --outdir " + pdfFile;
//            soffice --headless --invisible --convert-to pdf:writer_pdf_Export C:/pdf/测试1.doc --outdir C:/pdf/
            // FIXME: 2022/11/21 虽然设置了环境变量，还是得拼接完整路径的命令，不然执行不了
            command = libreofficeProperties.getHome() + "/program/soffice --headless --invisible --convert-to pdf:writer_pdf_Export " + srcPath + " --outdir " + desPath;
            log.info(command);
            windowExec(command);
        } else {
            command = " libreoffice --headless --invisible --convert-to pdf:writer_pdf_Export " + srcPath + " --outdir " + desPath;
            log.info(command);
            LinuxExec(command);
        }
        long end = System.currentTimeMillis();
        log.info("转换文件耗时：" + (end - start) + "毫秒");
    }

    /**
     * windows下执行转换命令
     *
     * @param command
     * @return
     */
    private boolean windowExec(String command) {
        Process process;// Process可以控制该子进程的执行或获取该子进程的信息
        try {
            process = Runtime.getRuntime().exec(command);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序，并返回与该子进程对应的Process对象实例。
            // 下面两个可以获取输入输出流
            InputStream errorStream = process.getErrorStream();
            InputStream inputStream = process.getInputStream();
        } catch (IOException e) {
            return false;
        }

        int exitStatus = 0;
        try {
            exitStatus = process.waitFor();// 等待子进程完成再往下执行，返回值是子线程执行完毕的返回值,返回0表示正常结束
            // 第二种接受返回值的方法
            int i = process.exitValue(); // 接收执行完毕的返回值
        } catch (InterruptedException e) {
            return false;
        }
        process.destroy(); // 销毁子进程
        process = null;
        return true;
    }

    /**
     * linux下执行转换命令
     *
     * @param cmd
     */
    private void LinuxExec(String cmd) {
        log.info(cmd);
        try {
            Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }
}
